class_name Animers
## 动画序列构建工具，支持链式调用和预设动画
## 
## 使用示例：
## [codeblock]
##   Animers.begin(node).then([anim1, anim2]).then_preset("FADE_IN")
## [/codeblock][codeblock]
##   Animers.begin(node).then(...).print_tween_script()
## [/codeblock]

## 必需的关键字集合
const REQUIRED_KEYS := ["property", "to", "duration"]
## 所有有效关键字集合
const VALID_KEYS := [
		"property", "to", "from", "duration", "delay", 
		"relative", "node", "ease", "trans",
		"method", "signals", "callback"
]
# 非动画关键字（特殊动作类型）
const NON_ANIM_KEYS := ["method", "signals", "callback"]
# 短名称映射字典
const SHORT_NAME_MAP := {
		"p": "property", "du": "duration", "de": "delay", 
		"r": "relative", "ts": "trans", "es": "ease"
}

# 预设动画配置
const PRESETS := {
		"FADE_IN": {"property": "modulate:a","from": 0.0,"to": 1.0,"duration": 0.5},
		"BOUNCE": {"property": "scale","from": Vector2(0.8, 0.8),"to": Vector2(1.1, 1.1),"duration": 0.3,"trans": Tween.TRANS_BOUNCE		},
		"UNFLOD":[
		# 第一阶段：纵向伸展 + 淡入（同步进行）
		[
		  # 轨道1 - 纵向伸展
			[{"property": "scale:y","from": 0.0,"to": 1.0,"duration": 0.8,"trans": Tween.TRANS_SINE,"ease": Tween.EASE_OUT}],
			[{"property": "scale:x","from": 0.2,"to": 0.2,"duration": 0.1,"trans": Tween.TRANS_SINE,"ease": Tween.EASE_OUT}],
		  # 轨道2 - 淡入效果
			[{"property": "modulate:a",	"from": 0.0,"to": 1.0,"duration": 0.6}]
			
		],
		[{"property": "scale:x","from": 0.2,"to": 1.0,"duration": 0.7,"trans": Tween.TRANS_BACK,"ease": Tween.EASE_OUT,"delay": 0.0 }],
		# 第三阶段：弹性微调（可选）
		#[
			#{"property": "scale","to": Vector2(0.93, 0.93),"duration": 0.2,"trans": Tween.TRANS_SPRING,"ease": Tween.EASE_OUT},
			#{"property": "scale","to": Vector2(1.0, 1.0),"duration": 0.3,"trans": Tween.TRANS_SPRING,"ease": Tween.EASE_OUT}
		#]
	]
}

var root_tween: Tween          # 主Tween对象
var root_node: Node            # 动画作用的根节点
var animation_history := []    # 动画历史记录
var special_actions_delay: float = 0.0  # 特殊动作产生的延迟累计
var generated_script_lines := []  # 存储生成的脚本行


## 静态入口函数：开始创建动画序列
static func begin(node: Node) -> Animers:
		var tweener = node.create_tween()
		return Animers.new(tweener, node)


## 添加动画到序列中（主入口）
func then(animations) -> Animers:
	#print("----")
	#print(animations)
	if typeof(animations) == TYPE_DICTIONARY:
			_process_single_animation(animations)
	elif typeof(animations) == TYPE_ARRAY:
		var arrsize=get_array_dimensions(animations)
		match arrsize:
			1:
				_process_animation_group(animations)
			2:
				_process_animation_group(animations)
			3:
				for i in animations:
					then(i)
					#print(i)
	return self  # 支持链式调用


## 构造函数
func _init(tween: Tween, node: Node) -> void:
		root_tween = tween
		root_node = node
		generated_script_lines.append("\tvar tween = create_tween()")


## 处理单动画配置
func _process_single_animation(animation_dict: Dictionary, delay_offset: float = 0.0, target_tween: Tween = null) -> float:
		var sanitized_dict = _sanitize_dictionary(animation_dict)
		var tween = target_tween if target_tween != null else root_tween
		#print(target_tween)
		# 记录动画用于脚本生成
		_record_animation_for_script(sanitized_dict, delay_offset, target_tween != null)
		
		# 处理特殊动作（方法/信号/回调）
		if _is_special_action(sanitized_dict):
				return _handle_special_action(sanitized_dict, tween, delay_offset)
		
		# 验证必需参数
		if not _validate_required_keys(sanitized_dict):
				return delay_offset
		
		# 创建属性动画
		return _create_property_tween(sanitized_dict, tween, delay_offset)


## 处理动画组（单轨或多轨动画）
func _process_animation_group(animations: Array) -> void:
		# 单轨道动画（顺序执行）
		if animations.size() > 0 and typeof(animations[0]) != TYPE_ARRAY:
				var cumulative_delay = 0.0
				for anim in animations:
					cumulative_delay = _process_single_animation(anim, cumulative_delay)
				return
		
		# 多轨道动画（并行执行）
		var sub_tween = root_node.create_tween()
		sub_tween.set_parallel(true)
		
		# 记录并行组开始
		generated_script_lines.append("\t# 并行动画组开始")
		generated_script_lines.append("\tvar parallel_tween = create_tween()")
		generated_script_lines.append("\tparallel_tween.set_parallel(true)")
		
		for track in animations:
				var track_delay = 0.0
				for anim in track:
						track_delay = _process_single_animation(anim, track_delay, sub_tween)
		
		sub_tween.set_parallel(false)
		root_tween.tween_subtween(sub_tween)
		
		# 记录并行组结束
		generated_script_lines.append("\tparallel_tween.set_parallel(false)")
		generated_script_lines.append("\ttween.tween_subtween(parallel_tween)")
		generated_script_lines.append("\t# 并行动画组结束")


## 获取目标节点
func _get_target_node(animation_dict: Dictionary) -> Node:
		if "node" not in animation_dict:
				return root_node
		
		var node_ref = animation_dict.node
		
		if typeof(node_ref) == TYPE_OBJECT:
				return node_ref as Node
		elif typeof(node_ref) == TYPE_NODE_PATH:
				var target = root_node.get_node_or_null(node_ref)
				return target if target != null else root_node
		
		push_error("Animers: Invalid node reference type")
		return root_node


## 验证必需参数是否存在
func _validate_required_keys(animation_dict: Dictionary) -> bool:
		for key in REQUIRED_KEYS:
				if key not in animation_dict:
						push_error("Animers: Missing required key '%s' in animation dict" % key)
						return false
		return true


## 创建属性动画
func _create_property_tween(animation_dict: Dictionary, tween: Tween, delay_offset: float) -> float:
		var target = _get_target_node(animation_dict)
		var property_path = animation_dict.property.replace(".", ":")
		animation_dict["property"] = property_path  # 更新字典中的属性名
		var property_tween = tween.tween_property(
				target,
				animation_dict.property,
				animation_dict.to,
				animation_dict.duration
		)
		#print(property_tween,tween)
		
		# 设置起始值
		if "from" in animation_dict:
				if typeof(animation_dict.from)==TYPE_STRING and animation_dict.from == "current":
						property_tween.from_current()
				else:
						property_tween.from(animation_dict.from)
		
		# 设置相对值模式
		if "relative" in animation_dict and animation_dict.relative:
				property_tween.as_relative()
		
		# 计算实际延迟（包含累计延迟）
		var actual_delay = delay_offset + animation_dict.get("delay", 0.0)
		property_tween.set_delay(actual_delay)
		
		# 设置缓动函数和过渡类型
		if animation_dict.has("ease"):
				property_tween.set_ease(animation_dict.ease)
		if animation_dict.has("trans"):
				property_tween.set_trans(animation_dict.trans)
		
		# 记录动画历史
		_record_animation(animation_dict)
		
		return actual_delay + animation_dict.duration


## 记录动画历史
func _record_animation(animation_dict: Dictionary) -> void:
		var record = animation_dict.duplicate()
		record["start_time"] = Time.get_ticks_msec()
		animation_history.append(record)


## 记录动画用于脚本生成
func _record_animation_for_script(animation_dict: Dictionary, delay_offset: float, is_parallel: bool) -> void:
		var prefix = "\tparallel_"
		#print(is_parallel)
		
		if _is_special_action(animation_dict):
				if "method" in animation_dict:
						var from_str = _value_to_code(animation_dict.get("from", null))
						var to_str = _value_to_code(animation_dict.get("to", null))
						generated_script_lines.append("%stween.tween_method(%s, %s, %s, %s)" % [
								"\t" if not is_parallel else prefix,
								animation_dict.method.get_method(),
								from_str,
								to_str,
								animation_dict.get("duration", 0.0)
						])
				elif "callback" in animation_dict:
						generated_script_lines.append("%stween.tween_callback(%s)" % [
								prefix,
								animation_dict.callback
						])
				return
		
		# 普通属性动画
		var target_node = _get_target_node(animation_dict)
		var node_path = "$"+str(root_node.get_path_to(target_node)) if target_node != root_node else "self"
		
		var line = "%stween.tween_property(%s, \"%s\", %s, %s)" % [
				"\t" if not is_parallel else prefix,
				node_path,
				animation_dict.property,
				_value_to_code(animation_dict.to),
				animation_dict.duration
		]
		
		# 添加额外参数
		if "from" in animation_dict:
				if typeof(animation_dict.from)==TYPE_STRING and animation_dict.from == "current":
						line += ".from_current()"
				else:
						line += ".from(%s)" % _value_to_code(animation_dict.from)
		
		if animation_dict.get("relative", false):
				line += ".as_relative()"
		
		if animation_dict.get("delay", 0.0) > 0 or delay_offset > 0:
				var total_delay = animation_dict.get("delay", 0.0) + delay_offset
				line += ".set_delay(%s)" % total_delay
		
		if animation_dict.get("ease") != null:
				line += ".set_ease(%s)" % animation_dict.ease
		
		if animation_dict.get("trans") != null:
				line += ".set_trans(%s)" % animation_dict.trans
		
		generated_script_lines.append(line)


## 将值转换为代码字符串表示
func _value_to_code(value) -> String:
		if value == null:
				return "null"
		
		match typeof(value):
				TYPE_STRING:
						return "\"%s\"" % value
				TYPE_VECTOR2:
						return "Vector2(%s, %s)" % [value.x, value.y]
				TYPE_VECTOR3:
						return "Vector3(%s, %s, %s)" % [value.x, value.y, value.z]
				TYPE_COLOR:
						return "Color(%s, %s, %s, %s)" % [value.r, value.g, value.b, value.a]
				TYPE_INT, TYPE_FLOAT, TYPE_BOOL:
						return str(value)
				_:
						return str(value)  # 复杂类型直接使用字符串表示


## 检查是否为特殊动作
func _is_special_action(animation_dict: Dictionary) -> bool:
		for key in NON_ANIM_KEYS:
				if key in animation_dict:
						return true
		return false


## 处理特殊动作（方法/信号/回调）
func _handle_special_action(animation_dict: Dictionary, tween: Tween, delay_offset: float) -> float:
		var actual_delay = delay_offset + animation_dict.get("delay", 0.0)
		
		if "method" in animation_dict:
				tween.tween_method(
						animation_dict.method,
						animation_dict.get("from", null),
						animation_dict.get("to", null),
						animation_dict.get("duration", 0.0)
				).set_delay(actual_delay)
				special_actions_delay = animation_dict.get("duration", 0.0)
				return actual_delay + animation_dict.get("duration", 0.0)
		
		elif "callback" in animation_dict:
				var callback = animation_dict.callback
				var args = animation_dict.get("args", [])
				
				if typeof(args) != TYPE_ARRAY:
						args = [args]
				
				tween.tween_callback(Callable(callback.bindv(args))).set_delay(actual_delay)
				return actual_delay
		
		# 信号处理预留
		elif "signals" in animation_dict:
				push_warning("Animers: Signal handling not implemented yet")
		
		return actual_delay


## 清洗字典：修正键名和值
func _sanitize_dictionary(original_dict: Dictionary) -> Dictionary:
		var sanitized = {}
		
		for key in original_dict:
				var corrected_key = _correct_key_name(key)
				sanitized[corrected_key] = original_dict[key]
		
		return sanitized


## 修正键名（检查拼写和短名称）
func _correct_key_name(key: String) -> String:
		# 检查是否有效键
		if key in VALID_KEYS:
				return key
		
		# 检查短名称映射
		if key in SHORT_NAME_MAP:
				return SHORT_NAME_MAP[key]
		
		# 寻找相似的有效键
		var closest_match = ""
		var highest_similarity = 0.0
		
		for valid_key in VALID_KEYS:
				var similarity = key.similarity(valid_key)
				if similarity > highest_similarity:
						highest_similarity = similarity
						closest_match = valid_key
		
		# 如果找到足够相似的键，使用它
		if highest_similarity > 0.6:
				push_warning("Animers: Corrected key '%s' to '%s'" % [key, closest_match])
				return closest_match
		
		return key  # 无法修正，返回原键


## 使用预设动画
func then_preset(preset_name: String, overrides: Dictionary = {}) -> Animers:
		if not preset_name in PRESETS:
				push_error("Animers: Unknown preset '%s'" % preset_name)
				return self
		
		var animation = PRESETS[preset_name].duplicate()
		if overrides.size()>0:animation.merge(overrides, true)  # 深度合并覆盖
		then(animation)
		return self


## 控制方法 ------------------------------------------------------

## 暂停动画
func pause() -> void:
		root_tween.pause()


## 播放动画
func play() -> void:
		root_tween.play()


## 停止动画
func stop() -> void:
		root_tween.stop()


## 重新开始动画
func restart() -> void:
		root_tween.stop()
		root_tween.play()


## 设置循环次数
func set_loops(loops: int = 0) -> Animers:
		root_tween.set_loops(loops)
		return self


## 打印动画历史
func print_history() -> void:
		for i in range(animation_history.size()):
				var anim = animation_history[i]
				print("Animation %d: %s" % [i, anim])


## 打印生成的Tween脚本
func print_tween_script(func_name:String="create_animations") -> void:
		generated_script_lines.insert(0,"func %s() -> void:"%func_name)
		# 添加结束注释
		generated_script_lines.append("\t# 动画序列结束")
		generated_script_lines.append("\t# 注: 此代码由Animers自动生成，可直接复制使用")
		
		# 打印所有脚本行
		for line in generated_script_lines:
				print(line)


## 获取生成的Tween脚本
func get_tween_script(func_name:String="create_animations") -> String:
		generated_script_lines.insert(0,"func %s() -> void:"%func_name)
		generated_script_lines.append("\t# 动画序列结束")
		generated_script_lines.append("\t# 注: 此代码由Animers自动生成，可直接复制使用")
		
		return "\n".join(generated_script_lines)

# 判断数组的维度
# 返回: 数组的维度数 (1为一维数组，2为二维数组，以此类推)
func get_array_dimensions(arr, current_depth = 1) -> int:
	if not arr is Array:
		return 0  # 不是数组
	
	if arr.size() == 0:
		return current_depth  # 空数组，返回当前深度
	
	var max_depth = current_depth
	for element in arr:
		if element is Array:
			var sub_depth = get_array_dimensions(element, current_depth + 1)
			if sub_depth > max_depth:
				max_depth = sub_depth
	
	return max_depth
